#include "base/eventloop.h"
#include "base/log.h"
#include "net/connect.h"

#include <sys/time.h>
#include "net/heartbeat.h"
namespace net_tools::net{

Heartbeat::Heartbeat(net_tools::Eventloop* eventloop,connect_func heartbeat_lose_func)
  :eventloop_(eventloop),
   heartbeat_lose_func_(std::move(heartbeat_lose_func)),
   time_index_(1),
   free_time_index_(0)
{
  uint64_t index = 1;
  for(int count = 0;count < 64;++count){
    index_vector_[count] = index;
    index = index << 1;
  }
  index_vector_[64] = 0x0000000000000000; //last one index_vector_[64] - 1 = 0x1111111111111111;
  for(int count = 0;count < _C::HEARTBEAT_VECTOR_SIZE;++count){
    heart_struct_[count] = new detail::heartbeat_struct();
  }
  Timeout();
};
void Heartbeat::Start(){
  timeval now_,sec_;
  gettimeofday(&now_,NULL);
  now_.tv_sec +=1;
  now_.tv_usec = 0;
  sec_.tv_sec = _C::HEARTBEAT_EVERY_SEC;
  sec_.tv_usec = _C::HEARTBEAT_EVERY_USEC;
  eventloop_->Run_every([this]{Timeout();},now_,sec_,-1,"Heartbeat");
};
void Heartbeat::Stop(){
  eventloop_->Deltimejob_every("Heartbeat");
};
void Heartbeat::Timeout(){
  ++time_index_;
  free_time_index_ = time_index_ - 1; //always add in before
  if(time_index_ == _C::HEARTBEAT_VECTOR_SIZE) time_index_ = 0;
  detail::heartbeat_struct* now_heart_struct = heart_struct_[time_index_];
  uint32_t SIZE = now_heart_struct->all_min_heartbeat_vector_.size();
  for(uint_fast16_t count = 0;count < SIZE;++count){
    detail::min_heartbeat_struct* now_min_heart_struct = now_heart_struct->all_min_heartbeat_vector_[count];
    uint8_t connect_number = now_min_heart_struct->SIZE;
    // 00001111 | 11110000 = 11111111 ~11111111 = 00000000
    uint64_t LOSE = ~( now_min_heart_struct->IS_ALIVE | (~((index_vector_[connect_number]) - 1)) );
    if(LOSE){
      uint64_t lowbit;
      for(uint_fast8_t index = 0;LOSE != 0;){
        lowbit = LOSE&(-LOSE);
        if(lowbit > 0x80){ //10000000;
          index += 8;
          LOSE = LOSE>>8;
          continue;
        }
        switch (lowbit)
        {
        case 0x01: index += 1;LOSE >>= 1;break;
        case 0x02: index += 2;LOSE >>= 2;break;
        case 0x04: index += 3;LOSE >>= 3;break;
        case 0x08: index += 4;LOSE >>= 4;break;
        case 0x10: index += 5;LOSE >>= 5;break;
        case 0x20: index += 6;LOSE >>= 6;break;
        case 0x40: index += 7;LOSE >>= 7;break;
        case 0x80: index += 8;LOSE >>= 8;break;
        }
        index -= 1;
        heartbeat_lose_func_(now_min_heart_struct->connect_vector[index]); //will call Lose_heartbeat
        --connect_number;//now for last one index
        if(index < connect_number){
            //last one move to lose connect index,only copy connect_vector;
            now_min_heart_struct->connect_vector[connect_number]->min_heartbeat_index_ = index;
            now_min_heart_struct->connect_vector[index] = now_min_heart_struct->connect_vector[connect_number];
        }
        index += 1;
        //over the last one,no need to move connect
      }//for(uint_fast8_t index = 0;LOSE != 0;)
    }//if(LOSE)
    now_min_heart_struct->IS_ALIVE = 0; //set 0,for next time call timeout
  }//for(uint_fast16_t count = 0;count < SIZE;++count)
};
void Heartbeat::Add_heartbeat(Connect* connect){
  if(connect->Is_add_heartbeat_){ // already in heartbeat
    return;
  }
  connect->heartbeat_ = this;
  connect->Is_add_heartbeat_ = true;
  detail::heartbeat_struct* heartbeat = heart_struct_[free_time_index_];
  detail::min_heartbeat_struct* free_heartbeat = *heartbeat->free_min_heartbeat_vector_.rbegin();
  connect->min_heartbeat_struct_ = free_heartbeat;
  connect->min_heartbeat_index_ = free_heartbeat->SIZE;
  free_heartbeat->connect_vector[free_heartbeat->SIZE] = connect;
  ++free_heartbeat->SIZE;
  if(free_heartbeat->SIZE == 64) [[unlikely]] {
    heartbeat->free_min_heartbeat_vector_.pop_back();
    if(!heartbeat->free_min_heartbeat_vector_.size()){
      heartbeat->No_Free_min_heartbeat_();
    }
  }
};
void Heartbeat::Del_heartbeat(Connect* connect){
  if(!connect->Is_add_heartbeat_){
    return;
  }
  connect->Is_add_heartbeat_ = false;
  detail::min_heartbeat_struct* min_heartbeat = connect->min_heartbeat_struct_;
  uint_fast8_t index = connect->min_heartbeat_index_;
  if(min_heartbeat->SIZE == 64) [[unlikely]] {
    connect->heartbeat_struct_->Add_free_min_heartbeat_(min_heartbeat);
  }
  --min_heartbeat->SIZE;
  if(!min_heartbeat->SIZE) [[unlikely]] {
    min_heartbeat->IS_ALIVE = 0;
    return;
  }
  // move IS_ALIVE, last one move to del_connect index
  uint64_t is_alive = min_heartbeat->IS_ALIVE;
  is_alive = (is_alive & (index_vector_[min_heartbeat->SIZE]));
  if(is_alive){
    min_heartbeat->IS_ALIVE = min_heartbeat->IS_ALIVE | (index_vector_[index]);
    min_heartbeat->IS_ALIVE = min_heartbeat->IS_ALIVE & (~(index_vector_[min_heartbeat->SIZE]));
  }else{
    min_heartbeat->IS_ALIVE = min_heartbeat->IS_ALIVE & (~(index_vector_[index]));
  }
  min_heartbeat->connect_vector[min_heartbeat->SIZE]->min_heartbeat_index_ = index;
  min_heartbeat->connect_vector[index] = min_heartbeat->connect_vector[min_heartbeat->SIZE];
};
void Heartbeat::Lose_heartbeat(Connect* connect){
  connect->Is_add_heartbeat_ = false;
  detail::min_heartbeat_struct* min_heartbeat = connect->min_heartbeat_struct_;
  if(min_heartbeat->SIZE == 64) [[unlikely]] {
    connect->heartbeat_struct_->Add_free_min_heartbeat_(min_heartbeat);
  }
  --min_heartbeat->SIZE;
};

} // namespace net_tools::net